#include "matrix.hpp"

const Matrix EMPTY_MATRIX(0, 0, nullptr);

Matrix Matrix::operator+(const Matrix &matrix) const
{
    if (this->m != matrix.m || this->n != matrix.n)
    {
        cerr << "The matrices with different dimensions";
        exit(EXIT_FAILURE);
    }
    int h = this->m;
    int w = this->n;
    int **newMatrixArray = (int **)malloc(h * sizeof(int *));
    for (int i = 0; i < h; i++)
    {
        newMatrixArray[i] = (int *)malloc(w * sizeof(int));
        for (int j = 0; j < w; j++)
        {
            newMatrixArray[i][j] = this->array[i][j] + matrix.array[i][j];
        }
    }
    Matrix answert(h, w, newMatrixArray);
    return answert;
}
Matrix Matrix::operator-(const Matrix &matrix) const
{
    if (this->m != matrix.m || this->n != matrix.n)
    {
        cerr << "The matrices with different dimensions";
        exit(EXIT_FAILURE);
    }
    int h = this->m;
    int w = this->n;
    int **newMatrixArray = (int **)malloc(h * sizeof(int *));
    for (int i = 0; i < h; i++)
    {
        newMatrixArray[i] = (int *)malloc(w * sizeof(int));
        for (int j = 0; j < w; j++)
        {
            newMatrixArray[i][j] = this->array[i][j] - matrix.array[i][j];
        }
    }
    Matrix answert(h, w, newMatrixArray);
    return answert;
}
Matrix Matrix::operator*(const Matrix &matrix) const
{
    if (this->n != matrix.m)
    {
        cerr << "The matrices with different dimensions";
        exit(EXIT_FAILURE);
    }
    int h = this->m;
    int w = matrix.n;
    int **newMatrixArray = (int **)malloc(h * sizeof(int *));
    for (int i = 0; i < h; i++)
    {
        newMatrixArray[i] = (int *)malloc(w * sizeof(int));
        for (int j = 0; j < w; j++)
        {
            newMatrixArray[i][j] = 0;
            for (int k = 0; k < this->n; k++)
            {
                newMatrixArray[i][j] += this->array[i][k] * matrix.array[k][j];
            }
        }
    }
    Matrix answert(h, w, newMatrixArray);
    return answert;
}
Matrix Matrix::transpose()
{
    int h = this->m;
    int w = this->n;
    int **newMatrixArray = (int **)malloc(w * sizeof(int *));
    for (int i = 0; i < w; i++)
    {
        newMatrixArray[i] = (int *)malloc(h * sizeof(int));
    }
    for (int i = 0; i < h; i++)
    {
        for (int j = 0; j < w; j++)
        {
            newMatrixArray[j][i] = this->array[i][j];
        }
    }
    Matrix answert(h, w, newMatrixArray);
    return answert;
}

string Matrix::format(int number)
{
    string str = to_string(number);
    return str;
}
void Matrix::printMatrix()
{

    for (int i = 0; i < this->m; i++)
    {
        for (int j = 0; j < this->n; j++)
        {
            cout << format(this->array[i][j]);
            if (j + 1 < this->n)
            {
                cout << '\t';
            }
        }
        cout << '\n';
    }
}

Matrix::Matrix(int m, int n, int **array)
{
    this->m = m;
    this->n = n;
    this->array = array;
}

Matrix::Matrix(string matrixString)
{
    int start = 0;
    int size = matrixString.size();
    int characterIndex = matrixString.find('\n');
    list<string> strlist;
    while (characterIndex > 0)
    {
        string line = matrixString.substr(start, characterIndex - start);
        start = characterIndex + 1;
        characterIndex = matrixString.find_first_of('\n', start);
        strlist.push_back(line);
    }
    if (characterIndex < size)
    {
        string line = matrixString.substr(start, size - start);
        strlist.push_back(line);
    }
    this->m = strlist.size();
    this->n = 1;
    string line = strlist.front();
    start = 0;
    characterIndex = line.find(',');
    while (characterIndex > 0)
    {
        this->n++;
        start = characterIndex + 1;
        characterIndex = line.find_first_of(',', start);
    }
    this->array = (int **)malloc(this->m * sizeof(int *));
    int i = 0;
    while (!strlist.empty())
    {
        line = strlist.front();
        characterIndex = line.find(',');
        if (characterIndex > 0)
        {
            this->array[i++] = parseLine(line, characterIndex);
        }
        strlist.pop_front();
    }
}

int *Matrix::parseLine(string line, int characterIndex)
{
    int start = 0;
    int *lineArray = (int *)malloc(this->n * sizeof(int));
    int i = 0;
    do
    {
        string numberstr = line.substr(start, characterIndex - start);
        lineArray[i++] = atoi(numberstr.data());
        start = characterIndex + 1;
        characterIndex = line.find_first_of(',', start);
        if (characterIndex < 0)
        {
            numberstr = line.substr(start, line.size() - start);
            lineArray[i++] = atoi(numberstr.data());
        }
    } while (characterIndex > 0);
    return lineArray;
}
